package com.qipay.resource.service.impl;

import com.qipay.resource.entity.Resource;
import com.qipay.resource.service.ImageHandleService;
import com.qipay.resource.service.ResourceService;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.UUID;

/**
 * Creator: Yao
 * Date:    2016/7/21
 * For:
 * Other:
 */
@Service
public class ResourceServiceImpl implements ResourceService {

    @Value("${upload.static.path:}")
    private String staticPath;
    @Value("${resource.static.url:}")
    private String staticUrl;
    @Value("${upload.thumbnail.prefix:}")
    private String thumbnailPrefix;
    @Value("${upload.img.ext:}")
    private String imgExts;
    @Value("${upload.doc.ext:}")
    private String docExts;
    @Value("${upload.img.max:2097152}")
    private int imgMaxSize;
    @Value("${default.user.avatar:}")
    private String defaultAvatarPath;


    @Autowired
    ImageHandleService imageHandleService;


    /**
     * correct Microsoft Office MIME types for HTTP Content Streaming
     * <p/>
     * .doc      application/msword
     * .dot      application/msword
     * <p/>
     * .docx     application/vnd.openxmlformats-officedocument.wordprocessingml.document
     * .dotx     application/vnd.openxmlformats-officedocument.wordprocessingml.template
     * .docm     application/vnd.ms-word.document.macroEnabled.12
     * .dotm     application/vnd.ms-word.template.macroEnabled.12
     * <p/>
     * .xls      application/vnd.ms-excel
     * .xlt      application/vnd.ms-excel
     * .xla      application/vnd.ms-excel
     * <p/>
     * .xlsx     application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
     * .xltx     application/vnd.openxmlformats-officedocument.spreadsheetml.template
     * .xlsm     application/vnd.ms-excel.sheet.macroEnabled.12
     * .xltm     application/vnd.ms-excel.template.macroEnabled.12
     * .xlam     application/vnd.ms-excel.addin.macroEnabled.12
     * .xlsb     application/vnd.ms-excel.sheet.binary.macroEnabled.12
     * <p/>
     * .ppt      application/vnd.ms-powerpoint
     * .pot      application/vnd.ms-powerpoint
     * .pps      application/vnd.ms-powerpoint
     * .ppa      application/vnd.ms-powerpoint
     * <p/>
     * .pptx     application/vnd.openxmlformats-officedocument.presentationml.presentation
     * .potx     application/vnd.openxmlformats-officedocument.presentationml.template
     * .ppsx     application/vnd.openxmlformats-officedocument.presentationml.slideshow
     * .ppam     application/vnd.ms-powerpoint.addin.macroEnabled.12
     * .pptm     application/vnd.ms-powerpoint.presentation.macroEnabled.12
     * .potm     application/vnd.ms-powerpoint.template.macroEnabled.12
     * .ppsm     application/vnd.ms-powerpoint.slideshow.macroEnabled.12
     * <p/>
     * <p/>
     * 补充：https://www.sitepoint.com/web-foundations/mime-types-complete-list/
     *
     * @param path
     * @return
     */
    @Override
    public String getContentType(String path) {

        Assert.isTrue(StringUtils.isNotBlank(path));

        String contentType = null;

        String ext = FilenameUtils.getExtension(path).toLowerCase();
        List<String> imgExtList = Arrays.asList(imgExts.split(","));
        List<String> docExtList = Arrays.asList(docExts.split(","));

        if (imgExtList.contains(ext)) {
            contentType = "image/png";
        } else if (docExtList.contains(ext)) {
            switch (ext) {
                case "doc":
                    contentType = "application/msword";
                    break;
                case ".docx":
                    contentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
                    break;
                default:
                    break;
            }
        } else
            switch (ext) {
                case "css":
                    contentType = "text/css";
                    break;
                case "js":
                    contentType = "application/javascript";
                    break;
                case "map":
                    contentType = "text/html";
                    break;
                case "html":
                    contentType = "text/html";
                    break;
                case "swf":
                    contentType = "application/x-shockwave-flash";
                    break;
            }

        return contentType;
    }

    /**
     * 获取指定资源的系统内路径
     *
     * @param fileName
     * @return
     */
    @Override
    public String getFileTypePath(String fileName) {

        return getFileTypePath(fileName, true);
    }

    /**
     * @param fileName
     * @param isFull   只获取 /opt/data/[static|auth] 之后的地址
     * @return
     */
    public String getFileTypePath(String fileName, boolean isFull) {

        Assert.isTrue(StringUtils.isNotBlank(fileName));

        String dir = "";
        String ext = FilenameUtils.getExtension(fileName).toLowerCase();
        List<String> imgExtList = Arrays.asList(imgExts.split(","));
        List<String> docExtList = Arrays.asList(docExts.split(","));

        String prePath = isFull ? staticPath : "";
        if (imgExtList.contains(ext) || docExtList.contains(ext)) {
            dir = prePath + "/";
        } else {
            throw new IllegalArgumentException("file type is not allow");
        }

        return dir;
    }

    /**
     * @param fileName   普通上传时代表文件时，需鉴权时代表资源ID
     * @param isThumbail 是否缩略图
     * @return
     */
    @Override
    public String generateUrl(String fileName, boolean isThumbail) {

        Assert.isTrue(StringUtils.isNotBlank(fileName));

        String url = String.format(staticUrl, this.getFileTypePath(fileName, false) + (isThumbail ? thumbnailPrefix : "") + fileName);

        return url;
    }

    /**
     * 目前只支持非鉴权图片资源
     *
     * @param fileName
     * @param width
     * @param height
     * @return
     */
    @Override
    public String generateUrl(String fileName, int width, int height) {

        Assert.isTrue(StringUtils.isNotBlank(fileName) && width > 0 && height > 0);

        String url = String.format(staticUrl, this.getFileTypePath(fileName, false) + String.format("%sx%s_", width, height) + fileName);

        return url;
    }

    @Override
    public boolean checkSize(Resource resource) {

        List<String> imgExtList = Arrays.asList(imgExts.split(","));
        String ext = FilenameUtils.getExtension(resource.getFileName()).toLowerCase();
        if (imgExtList.contains(ext) && resource.getFileSize() > imgMaxSize)
            return false;
        return true;
    }

    @Override
    public Resource upload(MultipartFile file) throws IOException {

        // check
        Assert.notNull(file, "file is null");

        // init
        File f;
        String filename = file.getOriginalFilename();
        Resource resource = new Resource();
        resource.setFileName(filename);
        resource.setFileSize(file.getSize());
        resource.setCreateTime(new Date());

        // init.path
        String path = this.getFileTypePath(filename);
        String extension = FilenameUtils.getExtension(filename);
        do {
            f = new File(path + UUID.randomUUID() + "." + extension.toLowerCase());
        } while (f.exists());
        resource.setFilePath(f.getAbsolutePath());
        f.getParentFile().mkdirs();

        // upload
        upload(file.getInputStream(), f);
        resource.setUrl(this.generateUrl(filename, false));

        return resource;
    }

    @Override
    @Transactional
    public Resource upload(InputStream is, Resource resource, boolean isThumbail) throws IOException {

        Assert.notNull(is, "input stream is null");
        Assert.notNull(resource, "the resource model is null");
        Assert.isTrue(StringUtils.isNotBlank(resource.getFileName()), "filename is empty.");

        UUID uuid;
        String filename;
        String path = this.getFileTypePath(resource.getFileName());
        String ext = FilenameUtils.getExtension(resource.getFileName());
        File file;
        do {
            uuid = UUID.randomUUID();
            filename = uuid + "." + ext.toLowerCase();
            file = new File(path + filename);
        } while (file.exists());

        resource.setFilePath(file.getAbsolutePath());
        file.getParentFile().mkdirs();

        upload(is, file);
        resource.setUrl(this.generateUrl(filename, isThumbail));

        if (isThumbail) {
            try {
                imageHandleService.thumbnail(file.getAbsolutePath(), path + thumbnailPrefix + filename);
            } catch (IOException e) {
                e.printStackTrace();
                throw new IOException();
            }
        }

        return resource;
    }

    @Override
    @Transactional
    public Resource upload(InputStream is, Resource resource, String fileName, int width, int height) throws IOException {

        Assert.notNull(is, "input stream is null");
        Assert.notNull(resource, "resource is null");
        Assert.isTrue(StringUtils.isNotBlank(resource.getFileName()) && StringUtils.isNoneBlank(fileName));

        String path = this.getFileTypePath(resource.getFileName(), false);
        String ext = "." + FilenameUtils.getExtension(resource.getFileName());
        fileName += ext;
        File file = new File(path + fileName);
        file.getParentFile().mkdirs();
        file.deleteOnExit();

        upload(is, file);
        resource.setUrl(this.generateUrl(fileName, width, height));

        try {
            imageHandleService.thumbnail(file.getAbsolutePath(), path + FilenameUtils.getName(resource.getUrl()), width, height);
        } catch (IOException e) {
            e.printStackTrace();
            throw new IOException();
        }

        return resource;
    }

    private void upload(InputStream is, File file) {

        // check
        Assert.notNull(is, "input stream is null");
        Assert.notNull(file, "file is null");

        // upload
        BufferedOutputStream os = null;
        try {
            os = new BufferedOutputStream(new FileOutputStream(file));
            IOUtils.copy(is, os);

        } catch (IOException e) {
            e.printStackTrace();
            throw new IllegalStateException("file upload fail");
        } finally {
            IOUtils.closeQuietly(is);
            IOUtils.closeQuietly(os);
        }
    }

    @Override
    public String getDefaultAvatar() {

        if (StringUtils.isNoneBlank(staticUrl) && StringUtils.isNotBlank(defaultAvatarPath))
            return String.format(staticUrl, defaultAvatarPath);
        return null;
    }
}
